/********************************************************************
	created:	2010/01/03
	created:	3:1:2010   13:51
	filename: 	qyFrustum.h
	author:		Xiong Jiayu
	copyright:  
	purpose:	
*********************************************************************/
#pragma once

#ifndef __QYFRUSTUM_H__
#define __QYFRUSTUM_H__

#include "qyMath.h"
#include "qyVector3.h"
#include "qyMatrix3.h"
#include "qyOBB.h"
#include "qyAABB.h"
#include "qyPlane.h"

/*
===========================================
struct Frustum
===========================================
*/
/*
struct Frustum
{
public:
	Vector3		m_vEyePos;
	Vector3		m_vForward;
	Vector3		m_vRight;
	Vector3		m_vUp;

	float		m_rFactor; 
	float		m_uFactor;

	float		m_fNearZ;
	float		m_fFarZ;

public:
				Frustum();
				Frustum( const Frustum &rhs );
	
	Frustum &	operator=( const Frustum &rhs );
	
	void		SetPerspective( const float fov, const float aspect, const float zNear, const float zFar );
	void		ResetOrietation( const Vector3 &vEyePos, const Vector3 &vForward, const Vector3 &vRight, const Vector3 &vUp );

	bool		CullPoint( const Vector3 &Point );
	bool		CullHull( const Vector3 *pVertices, const char nVertices );
	//bool		CullSphere( const Sphere &sphere );
	bool		CullAABB( const AABB &aabb );
	bool		CullOBB( const OBB &obb );
};

//-----------------------------------------------------------------------
QY_INLINE Frustum::Frustum()
{

}

//-----------------------------------------------------------------------
QY_INLINE Frustum::Frustum( const Frustum &rhs )
: m_vEyePos	( rhs.m_vEyePos )
, m_vForward( rhs.m_vForward )
, m_vRight	( rhs.m_vRight )
, m_vUp		( rhs.m_vUp )
, m_rFactor	( rhs.m_rFactor )
, m_uFactor	( rhs.m_uFactor )
, m_fNearZ	( rhs.m_fNearZ )
, m_fFarZ	( rhs.m_fFarZ )
{

}

//-----------------------------------------------------------------------
QY_INLINE Frustum & Frustum::operator=( const Frustum &rhs )
{
	memcpy( this, &rhs, sizeof( Frustum ) );
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE void Frustum::SetPerspective( const float fov, const float aspect, const float zNear, const float zFar )
{
	m_rFactor = tanf( cDEG2RAD * fov * 0.5f );
	m_uFactor = m_rFactor * aspect;
	m_fNearZ  = zNear;
	m_fFarZ   = zFar;
}

//-----------------------------------------------------------------------
QY_INLINE void Frustum::ResetOrietation( const Vector3 &vEyePos, const Vector3 &vForward, const Vector3 &vRight, const Vector3 &vUp )
{
	m_vEyePos = vEyePos;
	m_vForward = vForward;
	m_vRight = vRight;
	m_vUp = vUp;
}

//-----------------------------------------------------------------------
QY_INLINE bool Frustum::CullPoint( const Vector3 &point )
{
	Vector3 OP = point - m_vEyePos;
	float f = OP * m_vForward;
	
	// cull near/far plane
	if ( f < m_fNearZ || f > m_fFarZ )
		return false;

	// side
	float r = OP * m_vRight;
	float rLimit = m_rFactor * f;
	if ( r < -rLimit || rLimit < r )
		return false;
	
	float u = OP * m_vUp;
	float uLimit = m_uFactor * f;
	if ( u < -uLimit || uLimit < u )
		return false;

	return true;
}

//-----------------------------------------------------------------------
QY_INLINE bool Frustum::CullAABB( const AABB &aabb )
{
	Vector3 P;
	int nOutOfLeft=0, nOutOfRight=0, nOutOfFar=0, nOutOfNear=0, nOutOfTop=0, nOutOfBottom=0;
	bool bIsInRightTest, bIsInUpTest, bIsInFrontTest;

	Vector3 Corners[2];
	Corners[0] = aabb.GetMin() - m_vEyePos;
	Corners[1] = aabb.GetMax() - m_vEyePos;

	for (int i=0; i<8; ++i) {
		bIsInRightTest = bIsInUpTest = bIsInFrontTest = false;
		P.x=Corners[i&1].x;
		P.y=Corners[(i>>2)&1].y;
		P.z=Corners[(i>>1)&1].z;

		float r = m_vRight * P;
		float u = m_vUp * P;
		float f = m_vForward * P;

		if ( r< -m_rFactor * f ) ++nOutOfLeft;
		else if ( r > m_rFactor * f ) ++nOutOfRight;
		else bIsInRightTest=true;

		if ( u < - m_uFactor * f ) ++nOutOfBottom;
		else if ( u > m_uFactor * f ) ++nOutOfTop;
		else bIsInUpTest=true;

		if ( f < m_fNearZ ) ++nOutOfNear;		
		else if ( f > m_fFarZ ) ++nOutOfFar;
		else bIsInFrontTest=true;

		if (bIsInRightTest && bIsInFrontTest && bIsInUpTest) return true;
	}

	if (nOutOfLeft==8 || nOutOfRight==8 || nOutOfFar==8 || nOutOfNear==8 || nOutOfTop==8 || nOutOfBottom==8 ) return false;
	return true;
}

//-----------------------------------------------------------------------
QY_INLINE bool Frustum::CullOBB( const OBB &obb )
{
	Vector3 P;
	int nOutOfLeft=0, nOutOfRight=0, nOutOfFar=0, nOutOfNear=0, nOutOfTop=0, nOutOfBottom=0;
	bool bIsInRightTest, bIsInUpTest, bIsInFrontTest;
	const Vector3 &center = obb.GetCenter();
	const Vector3 &extent = obb.GetExtents();
	Vector3 Corners[8];
	Corners[0].Set( center.x+extent.x,  center.y+extent.y,  center.z+extent.z);
	Corners[1].Set( center.x+extent.x,  center.y+extent.y,  center.z-extent.z);
	Corners[2].Set( center.x+extent.x,  center.y-extent.y,  center.z+extent.z);
	Corners[3].Set( center.x+extent.x,  center.y-extent.y,  center.z-extent.z);
	Corners[4].Set( center.x-extent.x,  center.y+extent.y,  center.z+extent.z);
	Corners[5].Set( center.x-extent.x,  center.y+extent.y,  center.z-extent.z);
	Corners[6].Set( center.x-extent.x,  center.y-extent.y,  center.z+extent.z);
	Corners[7].Set( center.x-extent.x,  center.y-extent.y,  center.z-extent.z);

	for (int i(0); i<8; ++i) Corners[i] -= m_vEyePos;

	for (int i=0; i<8; ++i) {
		bIsInRightTest = bIsInUpTest = bIsInFrontTest = false;
		P=Corners[i];

		float r = m_vRight * P;
		float u = m_vUp * P;
		float f = m_vForward * P;

		if ( r< - m_rFactor * f ) ++nOutOfLeft;
		else if ( r > m_rFactor * f ) ++nOutOfRight;
		else bIsInRightTest=true;

		if ( u < - m_uFactor * f ) ++nOutOfBottom;
		else if ( u > m_uFactor * f ) ++nOutOfTop;
		else bIsInUpTest=true;

		if ( f < m_fNearZ ) ++nOutOfNear;		
		else if ( f > m_fFarZ ) ++nOutOfFar;
		else bIsInFrontTest=true;

		if (bIsInRightTest && bIsInFrontTest && bIsInUpTest) return true;
	}

	if (nOutOfLeft==8 || nOutOfRight==8 || nOutOfFar==8 || nOutOfNear==8 || nOutOfTop==8 || nOutOfBottom==8 ) return false;
	return true;
}
*/
//-----------------------------------------------------------------------

struct Frustum
{
	Plane m_plane[6];
	
	void	FromViewProjectionMatrix( const Matrix4 &mat );
	
	bool		CullPoint( const Vector3 &Point ) const;
	bool		CullHull( const Vector3 *pVertices, const char nVertices ) const;
	bool		CullSphere( const Vector3 &origin, const float radius ) const;
	//bool		CullSphere( const Sphere &sphere );
	bool		CullAABB( const AABB &aabb ) const;
	bool		CullOBB( const OBB &obb ) const;
};



#endif
